package com.ermace.robo.jumper.game;

import com.ermace.robo.jumper.game.Player.Position;

public class Line {

	private final double x1;
	private final double y1;
	private final double x2;
	private final double y2;
	
	private double slope;
	
	/**
	 * 
	 * @param x1
	 * 		  The x-coordinate of the start.
	 * @param y1
	 * 		  The y-coordinate of the start.
	 * @param x2
	 * 		  The x-coordinate of the end.
	 * @param y2
	 * 		  The y-coordinate of the start.
	 */
	public Line(double x1, double y1, double x2, double y2) {
		this.x1 = x1;
		this.y1 = y1;
		this.x2 = x2;
		this.y2 = y2;
		
		calculateSlope(x1, y1, x2, y2);
	}
	
	public Line(Position start, Position end) {
		x1 = start.getX();
		y1 = start.getY();
		x2 = end.getX();
		y2 = end.getY();
		

		calculateSlope(x1, y1, x2, y2);
	}
	
	/**
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 */
	private void calculateSlope(double x1, double y1, double x2, double y2) {
		try {
			slope = (y2 - y1)/(x2 - x1);
		} catch (ArithmeticException e) {
			slope = Double.POSITIVE_INFINITY;
		}
	}

	/**
	 * 
	 * @param  y
	 * 		   The y-coordinate of the point on the line
	 * @return The x-coordinate of the same point.
	 * 		   Double.NaN if the point is not in the boundaries of the line.
	 */
	public Double getXCorrespondingTo(double y) {
		if ((y < y1 && y < y2) || (y > y1 && y > y2))
			return Double.NaN;
		
		if (slope == 0) {
			return (x1 + x2) / 2;
		} else if (slope == Double.POSITIVE_INFINITY || slope == Double.NEGATIVE_INFINITY)
			return x1;
		
		return (y - y1) / slope + x1;
	}
	
	/**
	 * 
	 * @param  y
	 * 		   The y-coordinate of the point on the line
	 * @return The x-coordinate of the same point.
	 * 		   Double.NaN if the point is not in the boundaries of the line.
	 */
	public Double getYCorrespondingTo(double x) {
		if ((x < x1 && x < x2) || (x > x1 && x > x2))
			return Double.NaN;
		
		if (slope == 0) {
			return y1;
		} else if (slope == Double.POSITIVE_INFINITY || slope == Double.NEGATIVE_INFINITY)
			return (y1 + y2) / 2;
		
		return (x - x1) * slope + y1;
	}
	
	private boolean containsY(double y) {
		if ( (y <= y2 && y >= y1) || (y <= y1 && y >= y2) )
			return true;
		
		return false;
	}
	
	/**
	 * 
	 * 
	 * @param other
	 * @return The position of the intersection with the other line.
	 * 		   Null if there is no intersection
	 */
	public Position intersection(Line other) {
		if (slope == other.slope) //TODO
			return null;
		
		double x;
		
		if  (Double.isInfinite(slope)) {
			x = x1;
			Double y = other.getYCorrespondingTo(x);
			if (containsY(y))
				return new Position(x, y);
		}
		else if (Double.isInfinite(other.slope)) {
			x = other.x1;
			Double y = getYCorrespondingTo(x);
			if (other.containsY(y))
				return new Position(x, y);
		}
		else {
			x = (other.y1 - y1 + slope*x1 - other.slope * other.x1) / (slope - other.slope);
			
			Double y1 = other.getYCorrespondingTo(x);
			Double y2 = getYCorrespondingTo(x);
			
			if (y1.equals(Double.NaN) || (! y1.equals(y2)) )
				return null;
			
//		double y;
//		if (slope == 0 || slope == Double.POSITIVE_INFINITY || slope == Double.NEGATIVE_INFINITY)
//			y = other.getYCorrespondingTo(x);
//		else
//			y = getYCorrespondingTo(x);
//		
//		if (y == -1)
//			return null;

			return new Position(x, y1);
		}
		
		return null;
	}
}
